package com.sojson.config.transaction;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.TransactionInterceptor;

import com.sojson.aspect.PointcutsBase;
import com.sojson.constant.ConstantByMap;

import lombok.extern.slf4j.Slf4j;

/**
 * 事务配置
 * 
 * @author liu
 * @date 2020-11-02
 */
@Slf4j
@Aspect
@Configuration
public class TransactionConfig implements PointcutsBase {

    /** 事务切点配置 */
    private String aopPointcutExpression = "execution(* " + ConstantByMap.PACKAGE_BASE + "..*.*(..))";
    @Autowired
    private TransactionManager transactionManager;

    /**
     * 第一种方式
     * 
     * @return
     */
    // @Bean
    public TransactionInterceptor txAdvice() {

        /** 只读事务，不做提交处理 */
        DefaultTransactionAttribute txAttrRequiredReadonly = new DefaultTransactionAttribute();
        // 传播特性
        txAttrRequiredReadonly.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        // 只读事务
        txAttrRequiredReadonly.setReadOnly(true);

        /** 普通事务 */
        DefaultTransactionAttribute txAttrRequired = new DefaultTransactionAttribute();
        // 传播特性
        txAttrRequired.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        // 对哪些异常进行回滚
        txAttrRequired.rollbackOn(new Exception());

        /* 事务管理规则，声明具备事务管理的方法名 */
        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
        source.addTransactionalMethod("add*", txAttrRequired);
        source.addTransactionalMethod("save*", txAttrRequired);
        source.addTransactionalMethod("delete*", txAttrRequired);
        source.addTransactionalMethod("remove*", txAttrRequired);
        source.addTransactionalMethod("update*", txAttrRequired);
        source.addTransactionalMethod("exec*", txAttrRequired);
        source.addTransactionalMethod("set*", txAttrRequired);
        source.addTransactionalMethod("get*", txAttrRequiredReadonly);
        source.addTransactionalMethod("select*", txAttrRequiredReadonly);
        source.addTransactionalMethod("query*", txAttrRequiredReadonly);
        source.addTransactionalMethod("find*", txAttrRequiredReadonly);
        source.addTransactionalMethod("list*", txAttrRequiredReadonly);
        source.addTransactionalMethod("count*", txAttrRequiredReadonly);
        source.addTransactionalMethod("is*", txAttrRequiredReadonly);

        TransactionInterceptor transactionInterceptor = new TransactionInterceptor(transactionManager, source);
        log.info("TransactionInterceptor注入成功！");
        return transactionInterceptor;
    }

    /**
     * 利用AspectJExpressionPointcut设置切面=切点+通知（写成内部bean的方式）
     * 
     * @return
     */
    // @Bean
    public Advisor txAdviceAdvisor() {
        /*
         * 声明切点的面 切面（Aspect）:切面就是通知和切入点的结合。 通知和切入点共同定义了关于切面的全部内容——它的功能、在何时和何地完成其功能。
         */
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        /* 声明和设置需要拦截的方法,用切点语言描写 */
        pointcut.setExpression(aopPointcutExpression);
        /* 设置切面=切点pointcut+通知TxAdvice */

        DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor(pointcut, txAdvice());
        log.info("Advisor注入成功！");
        return defaultPointcutAdvisor;
    }

    // /**
    // * 第二种方式
    // *
    // * @return
    // */
    // @Bean(name = "txAdvice")
    // public TransactionInterceptor getAdvisor() throws Exception {
    // Properties properties = new Properties();
    // properties.setProperty("get*", "PROPAGATION_REQUIRED,-Exception,readOnly");
    // properties.setProperty("add*", "PROPAGATION_REQUIRED,-Exception");
    // properties.setProperty("save*", "PROPAGATION_REQUIRED,-Exception");
    // properties.setProperty("update*", "PROPAGATION_REQUIRED,-Exception");
    // properties.setProperty("delete*", "PROPAGATION_REQUIRED,-Exception");
    //
    // TransactionInterceptor transactionInterceptor = new
    // TransactionInterceptor(transactionManager, properties);
    // log.info("TransactionInterceptor注入成功！");
    // return transactionInterceptor;
    // }
    // @Bean
    // public BeanNameAutoProxyCreator txProxy() {
    // BeanNameAutoProxyCreator creator = new BeanNameAutoProxyCreator();
    // creator.setInterceptorNames("txAdvice");
    // creator.setBeanNames("*Service", "*ServiceImpl");
    // creator.setProxyTargetClass(true);
    // log.info("BeanNameAutoProxyCreator注入成功！");
    // return creator;
    // }

}